package com.rslai.commons.http.tools;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.*;
import org.apache.http.client.CookieStore;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.cookie.*;
import org.apache.http.protocol.HttpContext;

import java.io.IOException;
import java.util.List;

/**
 * Request Cookies 拦截器
 * Created by lrs on 2018/8/6.
 */
public class RequestCookiesInterceptor implements HttpRequestInterceptor, HttpResponseInterceptor {
    private final Log log = LogFactory.getLog(getClass());
    public static final RequestCookiesInterceptor instance = new RequestCookiesInterceptor();

    /**
     * 处理 response ，在 http client 收到返回后执行
     * @param response HttpResponse
     * @param context HttpContext
     * @throws HttpException HttpException
     * @throws IOException IOException
     */
    @Override
    public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
        // TODO Auto-generated method stub

    }

    /**
     * 处理 request，在 http client 发送请求前执行
     * @param request 请求的 request
     * @param context 正在使用上下文
     * @throws HttpException HttpException
     * @throws IOException IOException
     */
    @Override
    public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
        if (request == null) {
            throw new IllegalArgumentException("HTTP request may not be null");
        }
        if (context == null) {
            throw new IllegalArgumentException("HTTP context may not be null");
        }

        // 创建 CookieSpec
        CookieSpec cookieSpec = new RequestBrowserCompatSpec();
        if (cookieSpec == null) {
            this.log.debug("Cookie spec not specified in HTTP context");
            return;
        }
        // 从 context 中得到 cookie store
        CookieStore cookieStore = (CookieStore) context.getAttribute(ClientContext.COOKIE_STORE);
        if (cookieStore == null) {
            this.log.debug("Cookie store not specified in HTTP context");
            return;
        }
        // 从 context 中得到 CookieOrigin
        CookieOrigin cookieOrigin = (CookieOrigin) context.getAttribute(ClientContext.COOKIE_ORIGIN);
        if (cookieOrigin == null) {
            this.log.debug("Cookie origin not specified in HTTP context");
            return;
        }
        HeaderIterator it = request.headerIterator(SM.COOKIE);
        addCookie(it, cookieSpec, cookieOrigin, cookieStore);

        // 判断是否支持 Cookie version
        if (cookieSpec.getVersion() > 0) {
            // 如果支持将 set-cookie2 放入 header
            it = request.headerIterator(SM.SET_COOKIE2);
            addCookie(it, cookieSpec, cookieOrigin, cookieStore);
        }
    }

    /**
     * 添加 cookie
     * @param iterator HeaderIterator
     * @param cookieSpec CookieSpec
     * @param cookieOrigin CookieOrigin
     * @param cookieStore CookieStore
     */
    private void addCookie(final HeaderIterator iterator, final CookieSpec cookieSpec, final CookieOrigin cookieOrigin, final CookieStore cookieStore) {
        while (iterator.hasNext()) {
            Header header = iterator.nextHeader();
            try {
                List<Cookie> cookies = cookieSpec.parse(header, cookieOrigin);
                for (Cookie cookie : cookies) {
                    try {
                        cookieSpec.validate(cookie, cookieOrigin);
                        cookieStore.addCookie(cookie);

                        if (this.log.isDebugEnabled()) {
                            this.log.debug("Cookie accepted: \"" + cookie + "\". ");
                        }
                    } catch (MalformedCookieException ex) {
                        if (this.log.isWarnEnabled()) {
                            this.log.warn("Cookie rejected: \"" + cookie + "\". " + ex.getMessage());
                        }
                    }
                }
            } catch (MalformedCookieException ex) {
                if (this.log.isWarnEnabled()) {
                    this.log.warn("Invalid cookie header: \"" + header + "\". " + ex.getMessage());
                }
            }
        }
    }

}
